/*
 * Copyright (c) 2008, 2010
 *      Toni Spets <toni.spets@iki.fi>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "common.h"
#include "protocol.h"

#define BUF_SIZE 1024
char buf[1024];
uint16_t pos;
uint8_t seq = 0x00;

void protocol_null(uint16_t count)
{
    memset(buf+pos, 0, count);
    pos += count;
}

void protocol_repeat(int8_t d, uint16_t count)
{
    memset(buf+pos, d, count);
    pos += count;
}

void protocol_byte(int8_t d)
{
    *(buf+pos) = (char)d;
    pos++;
}

void protocol_short(int16_t d)
{
    memcpy(buf+pos, &d, sizeof(int16_t));
    pos += sizeof(int16_t);
}

void protocol_int(int32_t d)
{
    memcpy(buf+pos, &d, sizeof(int32_t));
    pos += sizeof(int32_t);
}

void protocol_string(char *d, uint16_t len)
{
    if(len)
    {
        memset(buf+pos, 0, len);
        memcpy(buf+pos, d, strlen(d));
        pos += len;
    }
    else
    {
        memcpy(buf+pos, d, strlen(d));
        pos += strlen(d);
    }
}

void protocol_ts_string(char *d, uint16_t len)
{
    memset(buf+pos, 0, len);
    memcpy(buf+pos, d, strlen(d));
    *(buf+pos+strlen(d)) = 0x0D;
    *(buf+pos+strlen(d)+1) = 0x0A;
    pos += len;
}

void protocol_flush(cncnet_client *client)
{
    net_send(sock, buf, pos, &client->addr);
    pos = 0;
}

void protocol_header(cncnet_client *client, uint8_t broadcast)
{
    /* our custom header, not used for our own packets */
    protocol_int(0x00000000);
    protocol_short(0x0000);
    protocol_byte(0x00);
    protocol_byte(0x01);

    if(client->game == GAME_CNC95)
    {
        protocol_byte(0x34);
        protocol_byte(0x12);
        protocol_byte(broadcast);
        protocol_byte(seq);
    }
    else if(client->game == GAME_RA1)
    {
        protocol_byte(0x35);
        protocol_byte(0x12);
        protocol_byte(broadcast);
        protocol_byte(seq);
    }
    else if(client->game == GAME_TS)
    {
        // 4 byte checksum...
        /*
        protocol_byte(0x05);
        protocol_byte(0xE2);
        protocol_byte(0xF8);
        protocol_byte(0xB9);
        */

        protocol_byte(0x35);
        protocol_byte(0x12);
        protocol_byte(broadcast);
        protocol_byte(0x8D);
    }

    if(seq == 0xFF)
        seq = 0x00;
    else
        seq++;
}

void protocol_ts_message(cncnet_client *client, char *name, char *message)
{
    if(client->game != GAME_TS)
        return;

    protocol_header(client, 0x00);
    protocol_byte(0x01);
    protocol_short(0x0000);
    protocol_byte(0x02);
    protocol_byte(0xAA);
    protocol_byte(0x0C);
    protocol_null(3);
    protocol_string(name, 43);
    protocol_ts_string(message, 112);
    protocol_byte(0x01);
    protocol_null(0x127);
    protocol_flush(client);
}

void protocol_ra1_message(cncnet_client *client, char *name, char *message)
{
    if(client->game != GAME_RA1)
        return;

    protocol_header(client, 0x01);
    protocol_int(0x00000000);
    protocol_byte(0xAA);
    protocol_byte(0x0C);
    protocol_byte(0x00);
    protocol_string(name, 12);
    protocol_string(message, 116);
    protocol_int(0x00000001);
    protocol_byte(0x00);
    protocol_flush(client);
}

void protocol_cnc_message(cncnet_client *client, char *name, char *message)
{
    if(client->game != GAME_CNC95)
        return;

    protocol_header(client, 0x01);
    protocol_null(13);
    protocol_byte(0x01);
    protocol_byte(0xAA);
    protocol_byte(0x0A);
    protocol_string(name, 12);
    protocol_string(message, 28);
    protocol_null(5);
    protocol_flush(client);
}

void protocol_message(cncnet_client *client, char *name, char *message)
{
    if(client->game == GAME_CNC95)
    {
        protocol_cnc_message(client, name, message);
    }
    else if(client->game == GAME_RA1)
    {
        protocol_ra1_message(client, name, message);
    }
    else if(client->game == GAME_TS)
    {
        protocol_ts_message(client, name, message);
    }
}
